#[cfg(test)]
mod thread {
    use std::thread;
    use std::time::Duration;

    use colored::Colorize;

    #[test]
    fn create() {
        thread::spawn(|| {
            for i in 1..10 {
                println!("{} {}", "spawned thread {}".red(), i);
                thread::sleep(Duration::from_millis(1));
            }
        });

        for i in 1..5 {
            println!("{} {}", "main thread! {}".green(), i);
            thread::sleep(Duration::from_millis(1));
        }
        // main 退出 spawned 退出
    }

    #[test]
    fn join() {
        let handle = thread::spawn(|| {
            for i in 1..10 {
                println!("{} {}", "spawned thread {}".red(), i);
                thread::sleep(Duration::from_millis(1));
            }
        });
        handle.join().expect("等待子线程失败");
        for i in 1..5 {
            println!("{} {}", "main thread! {}".green(), i);
            thread::sleep(Duration::from_millis(1));
        }
    }

    #[test]
    fn join_move() {
        let v = vec![1, 2, 3];
        //问题在于 Rust 无法确定新的线程会活多久（多个线程的结束顺序并不是固定的），所以也无法确定新线程所引用的 v 是否在使用过程中一直合法
        let handle = thread::spawn(move || {
            //使用move拿走所有权
            println!("Here's a vector: {:?}", v);
        });

        handle.join().unwrap();
    }
}

// 在系统编程中，操作系统提供了直接杀死线程的接口

/*

那么 Rust 中线程是如何结束的呢？答案很简单：线程的代码执行完，线程就会自动结束。但是如果线程中的代码不会执行完呢？那么情况可以分为两种进行讨论：

线程的任务是一个循环 IO 读取，任务流程类似：
    IO 阻塞，等待读取新的数据 -> 读到数据，处理完成 -> 继续阻塞等待 ··· -> 收到 socket 关闭的信号 -> 结束线程，
        在此过程中，绝大部分时间线程都处于阻塞的状态，因此虽然看上去是循环，CPU 占用其实很小，也是网络服务中最最常见的模型
线程的任务是一个循环，里面没有任何阻塞，包括休眠这种操作也没有，此时 CPU 很不幸的会被跑满
    而且你如果没有设置终止条件，该线程将持续跑满一个 CPU 核心，并且不会被终止，直到 main 线程的结束

*/

/*
    只有当真的需要处理一个值得用线程去处理的任务时，才使用线程
    让线程数等于 CPU 核心数是最好的。
    但是当你的任务大部分时间都处于阻塞状态时，就可以考虑增多线程数量 而且过多的线程也会导致线程上下文切换的代价过大，使用 async/await 的 M:N 并发模型

    虽然是无锁，但是内部是 CAS 实现，大量线程的同时访问，会让 CAS 重试次数大幅增加
    线程过多时，CPU 缓存的命中率会显著下降，同时多个线程竞争一个 CPU Cache-line 的情况也会经常发生
    大量读写可能会让内存带宽也成为瓶颈
    读和写不一样，无锁数据结构的读往往可以很好地线性增长，但是写不行，因为写竞争太大


*/

#[cfg(test)]
mod thread_dead {
    use std::sync::{Arc, Barrier};
    use std::thread;
    use std::time::Duration;

    #[test]
    fn main() {
        // 创建一个线程A
        let new_thread = thread::spawn(move || {
            // 再创建一个线程B
            thread::spawn(move || {
                loop {
                    println!("I am a new thread.");
                }
            })
        });

        // 等待新创建的线程执行完成
        new_thread.join().unwrap();
        println!("Child thread is finish!");

        // 睡眠一段时间，看子线程创建的子线程是否还在运行
        thread::sleep(Duration::from_millis(100));
    }

    #[test]
    fn barrier() {
        let mut handles = Vec::with_capacity(6);
        let barrier = Arc::new(Barrier::new(6));
        // 可以使用 Barrier 让多个线程都执行到某个点后，才继续一起往后执行
        for _ in 0..6 {
            let b = barrier.clone();
            handles.push(thread::spawn(move || {
                println!("before wait");
                b.wait(); // before wait 后增加了一个屏障，目的就是等所有的线程都打印出before wait后，各个线程再继续执行
                println!("after wait");
            }));
        }

        for handle in handles {
            handle.join().unwrap();
        }
    }
}

#[cfg(test)]
mod tlv {
    use std::cell::RefCell;
    use std::thread::LocalKey;

    // 线程局部变量(Thread Local Variable)
    #[test]
    fn tlv() {
        use std::cell::RefCell;
        use std::thread;
        // 使用 thread_local 宏可以初始化线程局部变量，然后在线程内部使用该变量的 with 方法获取变量值
        thread_local!(static FOO: RefCell<u32> = RefCell::new(1));

        // FOO 即是创建的线程局部变量，每个新的线程访问它时，都会使用它的初始值作为开始，各个线程中的 FOO 值彼此互不干扰。
        // 注意 FOO 使用 static 声明为生命周期为 'static 的静态变量。

        FOO.with(|f| {
            assert_eq!(*f.borrow(), 1);
            *f.borrow_mut() = 2;
        });

        // 每个线程开始时都会拿到线程局部变量的FOO的初始值
        let t = thread::spawn(move || {
            FOO.with(|f| {
                assert_eq!(*f.borrow(), 1);
                *f.borrow_mut() = 3;
            });
        });

        // 等待线程完成
        t.join().unwrap();

        // 尽管子线程中修改为了3，我们在这里依然拥有main线程中的局部值：2
        FOO.with(|f| {
            assert_eq!(*f.borrow(), 2);
        });
    }

    struct Foo;

    // 结构体中使用线程局部变量
    impl Foo {
        thread_local! {        static FOO: RefCell<usize> = RefCell::new(0); }
    }

    #[test]
    fn main() {
        Foo::FOO.with(|x| println!("{:?}", x));
    }
    thread_local! {  static FOOO: RefCell<usize> = RefCell::new(0);
    }

    // 通过引用的方式使用
    struct Bar {
        fooo: &'static LocalKey<RefCell<usize>>,
    }

    impl Bar {
        fn constructor() -> Self {
            Self { fooo: &FOOO }
        }
    }
}

// 条件变量(Condition Variables)经常和 Mutex 一起使用，可以让线程挂起，直到某个条件发生后再继续执行
#[cfg(test)]
mod cv {
    use std::sync::{Arc, Condvar, Mutex, Once};
    use std::thread;

    #[test]
    fn main() {
        let pair = Arc::new((Mutex::new(false), Condvar::new()));
        let pair2 = pair.clone();

        thread::spawn(move || {
            let (lock, cvar) = &*pair2;
            let mut started = lock.lock().unwrap();
            println!("changing started");
            *started = true;
            cvar.notify_one();
            //子线程获取到锁，并将其修改为 true，然后调用条件变量的 notify_one 方法来通知主线程继续执行
        });

        let (lock, cvar) = &*pair;

        let mut started = lock.lock().unwrap();
        while !*started {
            started = cvar.wait(started).unwrap(); //调用 wait 方法挂起等待子线程的通知，并释放了锁 started
        }

        println!("started changed");
    }
    //某个函数在多线程环境下只被调用一次，例如初始化全局变量，
    // 无论是哪个线程先调用函数来初始化，都会保证全局变量只会被初始化一次，随后的其它线程调用就会忽略该函数
    #[test]
    fn handle_once() {
        static mut VAL: usize = 0;
        static INIT: Once = Once::new();
        // 代码运行的结果取决于哪个线程先调用 INIT.call_once
        // 虽然代码具有先后顺序，但是线程的初始化顺序并无法被保证！因为线程初始化是异步的，且耗时较久
        let handle1 = thread::spawn(move || {
            /*
            执行初始化过程一次，并且只执行一次。
            如果当前有另一个初始化过程正在运行，线程将阻止该方法被调用。
            当这个函数返回时，保证一些初始化已经运行并完成，它还保证由执行的闭包所执行的任何内存写入都能被其他线程在这时可靠地观察到。
            */
            INIT.call_once(|| unsafe {
                VAL = 1;
            });
        });

        let handle2 = thread::spawn(move || {
            INIT.call_once(|| unsafe {
                VAL = 2;
            });
        });

        handle1.join().unwrap();
        handle2.join().unwrap();

        println!("{}", unsafe { VAL });
    }
}
